package com.tbynet.jwp.framework.core;

import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Page;
import org.phprpc.util.AssocArray;
import org.phprpc.util.PHPSerializer;

import java.util.*;

/**
 * Model基类 - 提供通用方法
 *
 * 核心职责：
 * 1. 提供通用的数据操作方法
 * 2. 序列化和反序列化支持
 * 3. 数据验证和转换
 * 4. 缓存和性能优化
 * 5. 元数据管理
 *
 * 设计原则：
 * - 提供可复用的通用方法
 * - 支持多种数据格式转换
 * - 统一的数据验证机制
 * - 完善的错误处理
 *
 * @param <M> Model类型
 * @author 佰亿互联
 * @version 1.0
 * @since 2025-11-04
 */
public abstract class JwpModel<M extends JwpModel<M>> extends Model<M> {

    /** 日志记录器 */
    protected final Log log = Log.getLog(this.getClass());

    /** 默认分页大小 */
    protected static final int DEFAULT_PAGE_SIZE = 20;

    /** 最大分页大小 */
    protected static final int MAX_PAGE_SIZE = 100;

    // ========== 通用数据操作方法 ==========

    /**
     * 安全获取字段值
     *
     * @param fieldName 字段名
     * @param defaultValue 默认值
     * @return 字段值
     */
    public String getStrSafely(String fieldName, String defaultValue) {
        try {
            String value = getStr(fieldName);
            return StrKit.isBlank(value) ? defaultValue : value;
        } catch (Exception e) {
            log.warn("安全获取字符串字段失败，字段: %s, 默认值: %s", fieldName, defaultValue, e);
            return defaultValue;
        }
    }

    /**
     * 安全获取整数字段值
     *
     * @param fieldName 字段名
     * @param defaultValue 默认值
     * @return 字段值
     */
    public Integer getIntSafely(String fieldName, Integer defaultValue) {
        try {
            Integer value = getInt(fieldName);
            return value != null ? value : defaultValue;
        } catch (Exception e) {
            log.warn("安全获取整数字段失败，字段: %s, 默认值: %s", fieldName, defaultValue, e);
            return defaultValue;
        }
    }

    /**
     * 安全获取长整数字段值
     *
     * @param fieldName 字段名
     * @param defaultValue 默认值
     * @return 字段值
     */
    public Long getLongSafely(String fieldName, Long defaultValue) {
        try {
            Long value = getLong(fieldName);
            return value != null ? value : defaultValue;
        } catch (Exception e) {
            log.warn("安全获取长整数字段失败，字段: %s, 默认值: %s", fieldName, defaultValue, e);
            return defaultValue;
        }
    }

    /**
     * 安全获取日期字段值
     *
     * @param fieldName 字段名
     * @param defaultValue 默认值
     * @return 字段值
     */
    public Date getDateSafely(String fieldName, Date defaultValue) {
        try {
            Date value = getDate(fieldName);
            return value != null ? value : defaultValue;
        } catch (Exception e) {
            log.warn("安全获取日期字段失败，字段: %s, 默认值: %s", fieldName, defaultValue, e);
            return defaultValue;
        }
    }

    /**
     * 安全获取布尔字段值
     *
     * @param fieldName 字段名
     * @param defaultValue 默认值
     * @return 字段值
     */
    public Boolean getBooleanSafely(String fieldName, Boolean defaultValue) {
        try {
            Boolean value = getBoolean(fieldName);
            return value != null ? value : defaultValue;
        } catch (Exception e) {
            log.warn("安全获取布尔字段失败，字段: %s, 默认值: %s", fieldName, defaultValue, e);
            return defaultValue;
        }
    }

    // ========== 数据验证方法 ==========

    /**
     * 验证必需字段
     *
     * @param fieldName 字段名
     * @param fieldLabel 字段标签
     * @return 验证是否通过
     */
    public boolean validateRequired(String fieldName, String fieldLabel) {
        String value = getStr(fieldName);
        if (StrKit.isBlank(value)) {
            log.warn("字段验证失败：%s不能为空", fieldLabel);
            return false;
        }
        return true;
    }

    /**
     * 验证字符串长度
     *
     * @param fieldName 字段名
     * @param fieldLabel 字段标签
     * @param minLength 最小长度
     * @param maxLength 最大长度
     * @return 验证是否通过
     */
    public boolean validateLength(String fieldName, String fieldLabel, int minLength, int maxLength) {
        String value = getStr(fieldName);
        if (StrKit.isBlank(value)) {
            log.warn("字段验证失败：%s不能为空", fieldLabel);
            return false;
        }
        if (value.length() < minLength || value.length() > maxLength) {
            log.warn("字段验证失败：%s长度应在%s-%s个字符之间", fieldLabel, minLength, maxLength);
            return false;
        }
        return true;
    }

    /**
     * 验证邮箱格式
     *
     * @param fieldName 字段名
     * @param fieldLabel 字段标签
     * @return 验证是否通过
     */
    public boolean validateEmail(String fieldName, String fieldLabel) {
        String value = getStr(fieldName);
        if (StrKit.isBlank(value)) {
            log.warn("字段验证失败：%s不能为空", fieldLabel);
            return false;
        }
        if (!value.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
            log.warn("字段验证失败：%s格式不正确", fieldLabel);
            return false;
        }
        return true;
    }

    /**
     * 验证数字范围
     *
     * @param fieldName 字段名
     * @param fieldLabel 字段标签
     * @param min 最小值
     * @param max 最大值
     * @return 验证是否通过
     */
    public boolean validateNumberRange(String fieldName, String fieldLabel, Number min, Number max) {
        Number value = getNumber(fieldName);
        if (value == null) {
            log.warn("字段验证失败：%s不能为空", fieldLabel);
            return false;
        }
        double doubleValue = value.doubleValue();
        if (doubleValue < min.doubleValue() || doubleValue > max.doubleValue()) {
            log.warn("字段验证失败：%s应在%s-%s之间", fieldLabel, min, max);
            return false;
        }
        return true;
    }

    /**
     * 获取数字字段值
     *
     * @param fieldName 字段名
     * @return 数字值
     */
    public Number getNumber(String fieldName) {
        try {
            Object value = get(fieldName);
            if (value instanceof Number) {
                return (Number) value;
            } else if (value instanceof String) {
                return Double.parseDouble((String) value);
            }
            return null;
        } catch (Exception e) {
            log.warn("获取数字字段失败，字段: %s", fieldName, e);
            return null;
        }
    }

    // ========== 序列化和反序列化方法 ==========

    /**
     * 序列化对象为PHP序列化格式
     *
     * @param data 要序列化的数据
     * @return 序列化后的字符串
     */
    protected String serializeToPhp(Object data) {
        try {
            PHPSerializer serializer = new PHPSerializer();
            byte[] bytes = serializer.serialize(data);
            return new String(bytes);
        } catch (Exception e) {
            log.error("PHP序列化失败", e);
            return "";
        }
    }

    /**
     * 反序列化PHP序列化字符串
     *
     * @param phpSerializedData PHP序列化字符串
     * @return 反序列化后的对象
     */
    protected Object unserializeFromPhp(String phpSerializedData) {
        try {
            if (StrKit.isBlank(phpSerializedData)) {
                return new AssocArray();
            }
            PHPSerializer serializer = new PHPSerializer();
            return serializer.unserialize(phpSerializedData.getBytes());
        } catch (Exception e) {
            log.error("PHP反序列化失败", e);
            return new AssocArray();
        }
    }

    /**
     * 序列化权限数据
     *
     * @param permissions 权限数据
     * @return 序列化后的字符串
     */
    protected String serializePermissions(Object... permissions) {
        try {
            AssocArray array = new AssocArray();
            for (Object permission : permissions) {
                if (permission instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> permissionMap = (Map<String, Object>) permission;
                    for (Map.Entry<String, Object> entry : permissionMap.entrySet()) {
                        array.set(entry.getKey(), entry.getValue());
                    }
                }
            }
            return serializeToPhp(array);
        } catch (Exception e) {
            log.error("序列化权限数据失败", e);
            return "";
        }
    }

    /**
     * 反序列化权限数据
     *
     * @param serializedData 序列化后的权限数据
     * @return 权限关联数组
     */
    protected AssocArray unserializePermissions(String serializedData) {
        try {
            Object result = unserializeFromPhp(serializedData);
            return result instanceof AssocArray ? (AssocArray) result : new AssocArray();
        } catch (Exception e) {
            log.error("反序列化权限数据失败", e);
            return new AssocArray();
        }
    }

    // ========== 数据转换方法 ==========

    /**
     * 转换为Map（排除敏感字段）
     *
     * @param excludeFields 要排除的字段
     * @return 转换后的Map
     */
    public Map<String, Object> toMap(String... excludeFields) {
        Map<String, Object> result = new HashMap<>();
        Set<String> excludeSet = new HashSet<>(Arrays.asList(excludeFields));

        // 添加默认排除的敏感字段
        excludeSet.add("password");
        excludeSet.add("user_pass");
        excludeSet.add("session_tokens");

        for (Map.Entry<String, Object> entry : _getAttrs().entrySet()) {
            if (!excludeSet.contains(entry.getKey())) {
                result.put(entry.getKey(), entry.getValue());
            }
        }
        return result;
    }

    /**
     * 转换为JSON字符串
     *
     * @param excludeFields 要排除的字段
     * @return JSON字符串
     */
    public String toJson(String... excludeFields) {
        try {
            Map<String, Object> map = toMap(excludeFields);
            return com.jfinal.kit.JsonKit.toJson(map);
        } catch (Exception e) {
            log.error("转换为JSON失败", e);
            return "%s";
        }
    }

    /**
     * 从Map填充数据
     *
     * @param data 数据Map
     * @param excludeFields 要排除的字段
     */
    public void populateFromMap(Map<String, Object> data, String... excludeFields) {
        if (data == null || data.isEmpty()) {
            return;
        }

        Set<String> excludeSet = new HashSet<>(Arrays.asList(excludeFields));
        for (Map.Entry<String, Object> entry : data.entrySet()) {
            if (!excludeSet.contains(entry.getKey())) {
                set(entry.getKey(), entry.getValue());
            }
        }
    }

    // ========== 字段操作方法 ==========

    /**
     * 获取所有字段名
     *
     * @return 字段名列表
     */
    public List<String> getFieldNames() {
        return new ArrayList<>(_getAttrs().keySet());
    }

    /**
     * 检查字段是否存在
     *
     * @param fieldName 字段名
     * @return 是否存在
     */
    public boolean hasField(String fieldName) {
        return _getAttrs().containsKey(fieldName);
    }

    /**
     * 获取字段类型
     *
     * @param fieldName 字段名
     * @return 字段类型
     */
    public String getFieldType(String fieldName) {
        Object value = get(fieldName);
        if (value == null) {
            return "null";
        }
        return value.getClass().getSimpleName();
    }

    /**
     * 清空所有字段值
     */
    public void clearAllFields() {
        _getAttrs().clear();
    }

    /**
     * 复制字段值到另一个Model
     *
     * @param target 目标Model
     * @param excludeFields 要排除的字段
     */
    public void copyFieldsTo(M target, String... excludeFields) {
        if (target == null) {
            return;
        }

        Set<String> excludeSet = new HashSet<>(Arrays.asList(excludeFields));
        for (Map.Entry<String, Object> entry : _getAttrs().entrySet()) {
            if (!excludeSet.contains(entry.getKey())) {
                target.set(entry.getKey(), entry.getValue());
            }
        }
    }

    // ========== 性能监控方法 ==========

    /**
     * 获取字段数量
     *
     * @return 字段数量
     */
    public int getFieldCount() {
        return _getAttrs().size();
    }

    /**
     * 获取数据大小估计（字节）
     *
     * @return 数据大小估计
     */
    public long estimateDataSize() {
        long size = 0;
        for (Map.Entry<String, Object> entry : _getAttrs().entrySet()) {
            size += estimateObjectSize(entry.getKey());
            size += estimateObjectSize(entry.getValue());
        }
        return size;
    }

    /**
     * 估计对象大小
     *
     * @param obj 对象
     * @return 大小估计（字节）
     */
    private long estimateObjectSize(Object obj) {
        if (obj == null) {
            return 0;
        }
        if (obj instanceof String) {
            return ((String) obj).length() * 2L; // 假设UTF-16编码
        } else if (obj instanceof Number) {
            return 8; // 大多数数字类型为8字节
        } else if (obj instanceof Date) {
            return 8; // Date对象大小
        } else if (obj instanceof Boolean) {
            return 1; // 布尔值大小
        }
        return 16; // 默认对象头大小
    }

    // ========== 分页相关方法 ==========

    /**
     * 转换为分页数据
     *
     * @param page 分页对象
     * @param <T> 数据类型
     * @return 分页数据Map
     */
    public static <T> Map<String, Object> toPageData(Page<T> page) {
        Map<String, Object> result = new HashMap<>();
        result.put("list", page.getList());
        result.put("pageNumber", page.getPageNumber());
        result.put("pageSize", page.getPageSize());
        result.put("totalPage", page.getTotalPage());
        result.put("totalRow", page.getTotalRow());
        return result;
    }

    // ========== 工具方法 ==========

    /**
     * 隐藏敏感信息
     *
     * @param str 原始字符串
     * @param start 开始保留位数
     * @param end 结束保留位数
     * @return 隐藏后的字符串
     */
    protected String hideSensitiveInfo(String str, int start, int end) {
        if (StrKit.isBlank(str) || str.length() <= start + end) {
            return "***";
        }
        String startStr = str.substring(0, start);
        String endStr = str.substring(str.length() - end);
        return startStr + "***" + endStr;
    }

    /**
     * 获取类名（简化版）
     *
     * @return 类名
     */
    public String getSimpleClassName() {
        return this.getClass().getSimpleName();
    }

    /**
     * 获取数据库表名
     *
     * @return 数据库表名，不会返回null
     */
    public String getTableName() {
        return _getTable().getName();
    }

    /**
     * 获取主键字段数组
     *
     * @return 主键字段名数组，至少包含一个元素
     */
    public String[] getPrimaryKey() {
        return _getTable().getPrimaryKey();
    }

}